/*
 * sender.c
 *
 *  Created on: 2013. 10. 16.
 *      Author: jphong
 */

#include "np.h"
#include "packet.h"

typedef enum {
	Wait_for_call, Wait_for_ACK, Closing
} State;

extern int DEBUG_ON; // print debug messages if set
static unsigned int ReTxTimeout = 100; // default timeout in msec
static char *localhost = "127.0.0.1"; // default receiver hostname
static char *port = "5678"; // default receiver port

//Packet buffers
static Packet sndPacket, rcvPacket;
/*
 * rdt3.0 sender
 */
int main(int argc, char **argv) {
	struct sockaddr_in peer;
	int fd, s;
	unsigned char sndSeq;
	Event event;
	State state;
	Packet *sndpkt = &sndPacket;
	Packet *rcvpkt = &rcvPacket;
	char *data = &(sndPacket.data[0]);
	char *hostname, *service;
	int opt;
	int rcvlen;
	int RE_FLAG = 0;

	// Command line option processing
	while ((opt = getopt(argc, argv, "dt:")) != -1) {
		switch (opt) {
		case 'd':
			DEBUG_ON = 1; // Debug mode
			break;
		case 't':
			ReTxTimeout = atoi(optarg);
			break;
		default:
			error(
					1,
					0,
					"Usage: %s [-d] [-t timeout] [<receiver name or IP address> <port number>]\n",
					argv[0]);
		}
	}
	if (optind == argc - 2) {
		hostname = argv[optind];
		service = argv[optind + 1];
	} else if (optind == argc) {
		hostname = localhost;
		service = port;
	} else
		error(
				1,
				0,
				"Usage: %s [-d] [-t timeout] [<receiver name or IP address> <port number>]\n",
				argv[0]);
	debug("To %s:%s\tTimeout = %dmsec\tDebugMode = %s\n", hostname, service,
			ReTxTimeout, DEBUG_ON ? "on" : "off");

	set_address(hostname, service, &peer, "udp"); // Set peer's IP and port

	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
		error(1, errno, "socket creation failed\n");

	connect(s, (struct sockaddr *) &peer, sizeof(peer));
	fd = fileno(stdin);

	// Init conditiion
	sndSeq = 0;
	state = Wait_for_call;

	// FSM
	for (;;) {
		int datalen, rcvlen;

		event = getEvent(fd, s);
		// Actions taken by State and Event
		switch (state) {
		// Wait_for_call state
		case Wait_for_call:
			switch (event) {
			case RDT_SEND:
				//stop_timer();
				if (RE_FLAG == 0) {
					if (fgets(data, MAX_DATA, stdin) != NULL) { // Get data from above

						sndpkt = make_pkt(DATA, sndSeq, &sndPacket, MAX_DATA);
						udt_send(s, sndpkt, (struct sockaddr *) &peer,
								sizeof(peer));
						rcvlen = read(s, rcvpkt, sizeof(Packet));
						print_pkt("<<", rcvpkt);

						start_timer(ReTxTimeout);

						state = Wait_for_ACK;
						event = RDT_RCV;
						break;
					} else { // EoF encountered. Close the session

						sndpkt = make_pkt(END, sndSeq, &sndPacket, 0);
						udt_send(s, sndpkt, (struct sockaddr *) &peer,
								sizeof(peer));
						//start_timer(ReTxTimeout);

						rcvlen = read(s, rcvpkt, sizeof(Packet));
						print_pkt("<<", rcvpkt);
						state = Closing;
						event = RDT_RCV;
						continue;
					}

				} else {
					debug("MOVE TIMEOUT to SEND\n");
					udt_send(s, sndpkt, (struct sockaddr *) &peer, sizeof(peer));
					rcvlen = read(s, rcvpkt, sizeof(Packet));
					print_pkt("<<", rcvpkt);

					start_timer(ReTxTimeout);

					state = Wait_for_ACK;
					event = RDT_RCV;
					break;
				}

			default:
				continue; // ignore the event
			}
			//Wait_for_ACK state
		case Wait_for_ACK:

			switch (event) {
			case RDT_RCV:
				if (rcvpkt->header.seq == sndSeq) {
					sndSeq = (sndSeq + 1) % 2;
					state = Wait_for_call;
					RE_FLAG = 0;
				}

				//continue;
				break;

			case TIMEOUT:
				debug("TIME OUT EVENT STATE!!\n");
				state = Wait_for_call;
				RE_FLAG = 1;
				break;
			default:
				continue;
			}
			// Closing state
			// wait an ACK. Sender shall stop when anything hear from receiver
		case Closing:
			switch (event) {
			case RDT_RCV:
				if (rcvpkt->header.seq == sndSeq) {
					exit(0);
				}

				continue;
			case TIMEOUT:

				continue;
			default:
				continue;
			} // End of inner switch
		} // End of outer switch

	} // End of for loop

} // End of main

